﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class LevelManager : Singleton<LevelManager> 
{
	protected LevelManager () {}

	public enum LevelOrder
	{
		Level_Garbage = 0,
	}

	public enum LevelType
	{
		FORWARDONLY = 0,
		LOCK,
		BACKTRACK
	}

	public delegate void Callback();
	public Callback m_OnLevelStart;

	public GameObject m_InitialLevel;
	public float m_NewLevelSpawnThreshold = 2f;

	[HideInInspector]
	public bool m_LockCheck = false;

	private int m_CurrentLevelOrder = 0;
	private int m_PreviousRandomLevelIndex = -1;
	private float m_MinCamX;
	private float m_MaxCamX;
	private Vector3 m_NewPosition;

	private List<GameObject> m_LevelsResourcesList;
	private List<LevelPartInfo> m_LevelsInView;
	private List<LevelPartInfo> m_LevelsInViewToCheck;
	private List<LevelPartInfo> m_AllLevelsPartInfo;
	public List<LevelPartInfo> AllLevelsPartInfo
	{
		get { return m_AllLevelsPartInfo; }
	}
	private LevelPartInfo m_CurrentLevel;
	public LevelPartInfo CurrentLevel
	{
		get { return m_CurrentLevel; }
	}
	
	private LevelPartInfo m_PreviousLevel;

	private int m_CurrentLevelIndex = 0;
	public int CurrentLevelIndex
	{
		get { return m_CurrentLevelIndex; }
	}

	private GameplayCamera m_GameCam;

	protected override void Awake()
	{
		base.Awake();

		m_LevelsResourcesList = new List<GameObject>();

		string levelOrder = System.Enum.GetNames(typeof(LevelOrder))[m_CurrentLevelOrder];

		Object[] levels = Resources.LoadAll("Levels/" + levelOrder);

		for(int i = 0; i < levels.Length; i++)
		{
			m_LevelsResourcesList.Add((GameObject)levels[i]);
		}

		m_LevelsInView = new List<LevelPartInfo>();
		m_LevelsInViewToCheck = new List<LevelPartInfo>();
		m_AllLevelsPartInfo = new List<LevelPartInfo>();
	}

	private void Start()
	{
		m_GameCam = RefManager.GameplayCamera;
		SpawnInitialRandomLevel(m_InitialLevel);
		InvokeRepeating("LateChecks", 0f, 0.5f);
	}

	private void Update()
	{	
		m_MinCamX = m_GameCam.LeftLimit;
		m_MaxCamX = m_GameCam.RightLimit;
	}

	#region SPAWN LEVEL
	private GameObject SpawnLevel(GameObject i_GO = null)
	{
		GameObject go = null;
		if(i_GO == null)
		{
			int randomLevel = Random.Range(0, m_LevelsResourcesList.Count);

			if(randomLevel == m_PreviousRandomLevelIndex)
			{
				randomLevel = randomLevel == 0 ? randomLevel + 1 : randomLevel - 1;			
			}

			m_PreviousRandomLevelIndex = randomLevel;
			go = (GameObject)Instantiate(m_LevelsResourcesList[randomLevel]);
		}
		else
		{
			go = (GameObject)Instantiate(i_GO);
		}

		go.transform.parent = transform;

		return go;
	}

	private void SpawnInitialRandomLevel(GameObject i_GO = null)
	{		
		GameObject go = SpawnLevel(i_GO);
		LevelPartInfo partInfo = go.GetComponent<LevelPartInfo>();
		
		Vector3 newPos = RefManager.GameplayCamera.aTransform.position;
		newPos.z = 0;
		
		float deltaOffsetX = partInfo.MinX - (m_GameCam.CameraExtents.MinX);
		newPos.x -= deltaOffsetX;

		partInfo.aTransform.position = newPos;
		
		CheckLevel(partInfo);
		//m_CurrentLevel = partInfo;
	}

	private void SpawnNewRandomLevel(Vector3 i_Pos, bool i_CalculateOffsetX = true)
	{
		GameObject go = SpawnLevel();
		LevelPartInfo partInfo = go.GetComponent<LevelPartInfo>();

		if(i_CalculateOffsetX)
		{
			float deltaOffsetX = partInfo.MaxX - (partInfo.m_StartBackgroundVisual.bounds.center.x + partInfo.m_EndBackgroundVisual.bounds.center.x);
			i_Pos.x += deltaOffsetX;
		}

		go.transform.position = i_Pos;

		CheckLevel(partInfo);
	}

	private void CheckLevel(LevelPartInfo i_Level)
	{		
		if(i_Level.MinX < m_MaxCamX)
		{
			m_LevelsInView.Add (i_Level);
		}
		else
		{
			m_LevelsInViewToCheck.Add (i_Level);
		}
		
		m_AllLevelsPartInfo.Add (i_Level);
	}
	#endregion

	#region LEVEL PARTS MANAGEMENT
	private void LateChecks()
	{	
		CheckNeedSpawn();
		if(!m_LockCheck) CheckLevelsInView();
		CheckFirst();
	}

	private void CheckNeedSpawn()
	{
		if(m_MaxCamX >= m_AllLevelsPartInfo[m_AllLevelsPartInfo.Count - 1].MaxX - m_NewLevelSpawnThreshold)
		{
			m_NewPosition.x = m_AllLevelsPartInfo[m_AllLevelsPartInfo.Count - 1].MaxX;
			m_NewPosition.y = 0;
			m_NewPosition.z = 0;
			SpawnNewRandomLevel(m_NewPosition, true);
		}
	}
	
	private void CheckLevelsInView()
	{
		int i;
		for(i = 0; i < m_LevelsInViewToCheck.Count; i++)
		{
			if(m_LevelsInViewToCheck[i].MinX < m_MaxCamX)
			{
				m_LevelsInView.Add (m_LevelsInViewToCheck[i]);
				m_LevelsInViewToCheck.RemoveAt(i);
			}
		}

		for(i = 0; i < m_LevelsInView.Count; i++)
		{
			if(m_LevelsInView[i].MaxX <= m_MaxCamX)
			{
				m_LevelsInView[i].OnLevelEndReached();
			}
		}

		if(m_PreviousLevel != m_LevelsInView[m_LevelsInView.Count - 1])
		{
			m_CurrentLevel = m_LevelsInView[m_LevelsInView.Count - 1];

			if(m_CurrentLevel.m_Type != LevelType.FORWARDONLY) 
			{
				m_LockCheck = true;
			}

			RefManager.GameplayCamera.SetBehaviour(m_CurrentLevel.m_Type);
			m_CurrentLevel.OnLevelStart();
			Debug.Log (m_CurrentLevel.name);
			
			m_PreviousLevel = m_CurrentLevel;
		}
	}

	private void CheckFirst()
	{
		if(m_AllLevelsPartInfo[0].MaxX < m_MinCamX)
		{
			m_LevelsInView.Remove (m_AllLevelsPartInfo[0]);

			Destroy(m_AllLevelsPartInfo[0].aGameObject);
			m_AllLevelsPartInfo.RemoveAt(0);
			m_CurrentLevelIndex--;
		}
	}
	#endregion
}
